/*
 * DOMHelper.java
 *
 * Created on November 3, 2007, 4:43 AM
 *
 * To change this template, choose Tools | Template Manager
 * and open the template in the editor.
 */
package custom.xml;

import java.io.File;
import java.io.IOException;

import java.net.URL;

import javax.xml.XMLConstants;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.validation.Schema;
import javax.xml.validation.SchemaFactory;
import javax.xml.validation.Validator;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerException;
import javax.xml.transform.TransformerConfigurationException;
import javax.xml.transform.Source;
import javax.xml.transform.Result;
import javax.xml.transform.OutputKeys;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.stream.StreamResult;
import org.xml.sax.SAXParseException;
import org.xml.sax.SAXException;
import org.w3c.dom.Document;
import org.w3c.dom.Element;

/**
 *
 * @author david
 */
public class DOMHelper {

    private Document document = null;
    private DocumentBuilder parser = null;
    private Schema schema = null;
    private Validator validator = null;
    private Transformer transformer = null;

    /**
     * Creates a new instance of DOMHelper
     */
    public DOMHelper(final URL xsdURL) throws DOMHelperException {
        this(xsdURL, null);
    }

    public DOMHelper(final URL xsdURL, final File xmlFile) throws DOMHelperException {
        if (xsdURL == null) {
            throw new IllegalArgumentException("DOMHelper constructor was passed a null for the xsdFile File object");
        }
        try {
            DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
            dbf.setNamespaceAware(true);
            parser = dbf.newDocumentBuilder();

            SchemaFactory sf = SchemaFactory.newInstance(XMLConstants.W3C_XML_SCHEMA_NS_URI);
            schema = sf.newSchema(xsdURL);
            validator = schema.newValidator();
            transformer = initTransformer();

            document = (xmlFile != null && xmlFile.exists()) ? validate(xmlFile) : newDocument();
        } catch (ParserConfigurationException pce) {
            throw new DOMHelperException("DOMHelper constructor experienced a ParserConfigurationException", pce);
        } catch (SAXException sxe) {
            throw new DOMHelperException("DOMHelper constructor experienced a SAXException", sxe);
        }
    }

    public DOMHelper(final File xmlFile) throws DOMHelperException {
        try {
            DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
            dbf.setNamespaceAware(true);
            parser = dbf.newDocumentBuilder();

            transformer = initTransformer();

            document = (xmlFile != null && xmlFile.exists()) ? parse(xmlFile) : newDocument();
        } catch (ParserConfigurationException pce) {
            throw new DOMHelperException("DOMHelper constructor experienced a ParserConfigurationException", pce);
        }
    }

    public Document newDocument() {
        document = parser.newDocument();
        return document;
    }

    public Document validate(final File xmlFile) throws DOMHelperException {
        document = parse(xmlFile);
        validate();
        return document;
    }

    public Document parse(final File xmlFile) throws DOMHelperException {
        if (xmlFile == null) {
            throw new IllegalArgumentException("DOMHelper.validate() was passed a null for the xmlFile File object");
        }
        try {
            document = parser.parse(xmlFile);
        } catch (IOException ioe) {
            throw new DOMHelperException("DOMHelper.parse() experienced an IOException", ioe);
        } catch (SAXParseException sxpe) {
            throw new DOMHelperException("DOMHelper.parse() experienced a SAXParseException", sxpe);
        } catch (SAXException sxe) {
            throw new DOMHelperException("DOMHelper.parse() experienced a SAXException", sxe);
        }
        return document;
    }

    public void validate() throws DOMHelperException {
        try {
            validator.validate(new DOMSource(document));
        } catch (IOException ioe) {
            throw new DOMHelperException("DOMHelper.validate() experienced an IOException", ioe, document.getDocumentURI());
        } catch (SAXParseException sxpe) {
            throw new DOMHelperException("DOMHelper.validate() experienced a SAXParseException", sxpe, document.getDocumentURI());
        } catch (SAXException sxe) {
            throw new DOMHelperException("DOMHelper.validate() experienced a SAXException", sxe, document.getDocumentURI());
        }
    }

    public Document getDocument() {
        return document;
    }

    public Element buildSimpleElement(final String tag, final String value) {
        Element element = document.createElement(tag);
        if (value != null && value.length() > 0) {
            element.appendChild(document.createTextNode(value));
        }
        return element;
    }

    public void saveToDisk(final File fileDest) throws DOMHelperException {
        Source src = new DOMSource(document);
        Result res = new StreamResult(fileDest);
        try {
            transformer.transform(src, res);
        // Call parse to sync up the document with what was saved to disk.
        //parse(fileDest);
        } catch (TransformerException e) {
            throw new DOMHelperException("DOMHelper.saveToDisk encountered a TransformerException.", e, document.getDocumentURI());
        }
    }

    public void print() throws DOMHelperException {
        Source src = new DOMSource(document);
        Result res = new StreamResult(System.out);
        try {
            transformer.transform(src, res);
        } catch (TransformerException e) {
            throw new DOMHelperException("DOMHelper.print encountered a TransformerException.", e);
        }
    }

    private Transformer initTransformer() throws DOMHelperException {
        if (transformer == null) {
            try {
                transformer = TransformerFactory.newInstance().newTransformer();
                // The following two lines are necessary for the xml data to be indented in the file.
                transformer.setOutputProperty(OutputKeys.INDENT, "yes");
                transformer.setOutputProperty("{http://xml.apache.org/xslt}indent-amount", "2");
            } catch (TransformerConfigurationException e) {
                throw new DOMHelperException("DOMHelper.initTransformer encountered a TransformerConfigurationException", e);
            }
        }
        return transformer;
    }
}
